package org.cosmact.designer.vxml.gefeditor.router;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.cosmact.common.utils.Utilities;
import org.cosmact.vxml.model.Dialog;
import org.cosmact.vxml.model.Document;
import org.cosmact.vxml.model.EDialogType;
import org.eclipse.emf.common.util.EList;

/**
 * Latest pathes coverd all dialogs in document.
 * 
 *@author hsieh 234aini@gmail.com
 * 
 */
public class LatestPathRouteStrategy implements IRouteStrategy {

    private final Document document;
    private Set<Dialog> unCoverDialogs;

    public LatestPathRouteStrategy(final Document document) {
	if (document == null)
	    throw new IllegalArgumentException("Document");
	this.document = document;
    }

    @Override
    public List<Path> getCoverPathes() {
	List<Path> pathes = findAllPathes();
	if (Utilities.isNullOrEmpty(pathes)) {
	    return pathes;
	}

	// First sort the path by path length.
	Collections.sort(pathes, new Comparator<Path>() {
	    @Override
	    public int compare(Path o1, Path o2) {
		return o2.getNodes().size() - o1.getNodes().size();
	    }
	});

	final List<Path> selectedPathes = new ArrayList<Path>();
	unCoverDialogs = new HashSet<Dialog>();
	unCoverDialogs.addAll(document.getDialogs());
	// add longest path to cover pathes
	Path selectedPath = pathes.get(0);
	selectedPathes.add(selectedPath);
	unCoverDialogs.removeAll(selectedPath.getNodes());

	pathes.remove(selectedPath);
	while (unCoverDialogs.size() > 0) {
	    Path nextPath = findCoverLagestPath(pathes);
	    if (nextPath == null)
		break;
	    selectedPathes.add(nextPath);
	    unCoverDialogs.removeAll(nextPath.getNodes());
	    pathes.remove(nextPath);
	}

	return selectedPathes;
    }

    private Path findCoverLagestPath(List<Path> pathes) {
	if (Utilities.isNullOrEmpty(pathes)) {
	    return null;
	}
	int[] numberes = new int[pathes.size()];
	for (int i = 0; i < numberes.length; i++) {
	    numberes[i] = getUnCoveredNumberInPath(pathes.get(i));
	}

	int pathIndex = Utilities.getLocationForMaxValue(numberes);
	if (numberes[pathIndex] > 0) {// have at least un covered dialog
	    return pathes.get(pathIndex);
	}
	return null;
    }

    private int getUnCoveredNumberInPath(Path path) {
	int i = 0;
	for (Dialog dialog : path.getNodes()) {
	    if (unCoverDialogs.contains(dialog)) {
		i++;
	    }
	}
	return i;
    }

    @SuppressWarnings("unchecked")
    private List<Path> findAllPathes() {
	EList<Dialog> dialogs = document.getDialogs();
	if (dialogs == null || dialogs.size() <= 0)
	    return Collections.EMPTY_LIST;
	List<Path> pathes = new ArrayList<Path>();
	for (Dialog dialog : dialogs) {
	    if (isStartDialog(dialog)) {
		Router router = new Router(dialog);
		pathes.addAll(router.getPathes());
	    }
	}
	return pathes;
    }

    /**
     * A dialog with no target connection is a Start dialog.
     * 
     * @param dialog
     * @return
     */
    private boolean isStartDialog(Dialog dialog) {
	if (dialog.isStartDialog()) {
	    return true;
	}
	//Catch dialog is alwyas a start node.
	if (dialog.getDialogType().getType() == EDialogType.CATCH) {
	    return true;
	}
	return Utilities.isNullOrEmpty(dialog.getTargetConnections());
    }
}
